package com.xayq.orap.oauth;

import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import com.xayq.orap.mapper.UserMapper;
import com.xayq.orap.model.AccessToken;
import com.xayq.orap.model.ClientDetails;
import com.xayq.orap.model.User;
import com.xayq.orap.service.ClientService;
import com.xayq.orap.service.OAuthService;
import com.xayq.orap.utils.CommonUtils;

import javax.servlet.http.HttpServletResponse;

@Component
public abstract class OAuthHandler {


    private static final Logger LOG = LoggerFactory.getLogger(OAuthHandler.class);

//    protected transient OAuthService OAuthService = BeanProvider.getBean(OauthService.class);

    @Autowired
    protected ClientService clientService;
    
    @Autowired
    protected OAuthService oAuthService;
    
    @Autowired
    protected UserMapper userMapper;
    
    @Autowired
    @Qualifier("authenticationManager")
    protected AuthenticationManager authenticationManager;
    
    protected ClientDetails clientDetails;
    
    protected HttpServletResponse response;


    protected ClientDetails clientDetails() {

        String clientId = getClientId();
        clientDetails = clientService.getClientDetailsByClientId(clientId);
        LOG.debug("Load ClientDetails: {} by clientId: {}", clientDetails, clientId);

        return clientDetails;
    }


    protected OAuthResponse createTokenResponse(AccessToken accessToken, boolean queryOrJson) throws OAuthSystemException {
        ClientDetails clientDetails = clientDetails();

        OAuthASResponse.OAuthTokenResponseBuilder builder = OAuthASResponse
                .tokenResponse(HttpServletResponse.SC_OK)
                .location(clientDetails.getRedirectUri())
                .setAccessToken(accessToken.getToken())
                .setExpiresIn(String.valueOf(accessToken.currentTokenExpiredSeconds()))
                .setTokenType(accessToken.getTokenType());

        String refreshToken = accessToken.getRefreshToken();
        if (StringUtils.isNotEmpty(refreshToken)) {
            builder.setRefreshToken(refreshToken);
        }

        return queryOrJson ? builder.buildQueryMessage() : builder.buildJSONMessage();
    }
    
    protected OAuthResponse invalidClientErrorResponse() throws OAuthSystemException {
        return OAuthResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                .setError(OAuthError.TokenResponse.INVALID_CLIENT)
                .setErrorDescription("Invalid client_id '" + getClientId() + "'")
                .buildJSONMessage();
    }
    
    protected OAuthResponse invalidRedirectUriResponse() throws OAuthSystemException {
        return OAuthResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                .setError(OAuthError.CodeResponse.INVALID_REQUEST)
                .setErrorDescription("Invalid redirect_uri '" + getRedirectURI() + "'")
                .buildJSONMessage();
    }

    protected OAuthResponse invalidScopeResponse() throws OAuthSystemException {
        return OAuthResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
                .setError(OAuthError.CodeResponse.INVALID_SCOPE)
                .setErrorDescription("Invalid scope '" + getScopes() + "'")
                .buildJSONMessage();
    }

    protected OAuthResponse invalidClientSecretResponse() throws OAuthSystemException {
        return OAuthResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                .setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
                .setErrorDescription("Invalid client_secret by client_id '" + getClientId() + "'")
                .buildJSONMessage();
    }
    
    protected OAuthResponse invalidGrantTypeResponse(String grantType) throws OAuthSystemException {
        return OAuthResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
                .setError(OAuthError.TokenResponse.INVALID_GRANT)
                .setErrorDescription("Invalid grant_type '" + grantType + "'")
                .buildJSONMessage();
    }
    
	protected boolean excludeScopes(Set<String> scopes, ClientDetails clientDetails) {
        Map<String, Set<String>> allowedScope = new HashMap<String, Set<String>>();
    	String clientScopes = clientDetails.getScope();
        for(String clientScope:clientScopes.split(";")){
        	Set<String> value = new HashSet<String>();
        	if(clientScope.split("@").length > 1){
        		CollectionUtils.addAll(value,clientScope.split("@")[1].split(","));
        	}
        	allowedScope.put(clientScope.split("@")[0],value);
        }

        for (String scope : scopes) {
        	String scopeKey = scope.split("@")[0];
        	Set<String> scopeValue = new HashSet<String>();
        	if(scope.split("@").length > 1){
        		CollectionUtils.addAll(scopeValue,scope.split("@")[1].split(","));
        	}
        	if(allowedScope.containsKey(scopeKey)&&
        			((scopeValue.isEmpty()&&allowedScope.get(scopeKey).isEmpty())||
        			((!scopeValue.isEmpty())&&allowedScope.get(scopeKey).containsAll(scopeValue)))
        	){
        		
        	}else{
                LOG.debug("Invalid scope - ClientDetails scopes '{}' exclude '{}'", clientScopes, scope);
                return true;
            }
        }
        return false;
    }
    
    public OAuthResponse validate() throws OAuthSystemException {
    	ClientDetails clientDetails = clientDetails();
		if (clientDetails == null) {
			 return invalidClientErrorResponse();
        }
		return validateSelf();
    }
    
    protected boolean checkValidateResponse(OAuthResponse oAuthResponse) {
        if (oAuthResponse != null) {
            LOG.debug("Validate OAuthAuthzRequest(client_id={}) failed", getClientId());
            CommonUtils.writeOAuthJsonResponse(response, oAuthResponse);
            return true;
        }
        return false;
    }


    protected abstract String getClientId();
    protected abstract String getRedirectURI();
    protected abstract Set<String> getScopes();
    protected abstract OAuthResponse validateSelf() throws OAuthSystemException;
}
